/*************************************************************************
	> File Name: AwinicAPI.c
	> Author: 
	> Mail: 
	> Created Time: 2018/1/23 Tue 18:32
	> Version     :
 ************************************************************************/

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdbool.h>

#include "AwinicLog.h"
#include "AwinicMec.h"
#include "AwinicAPI.h"


typedef struct awinicCfg{
   awinicMecCfg_t leftChCfg;
   awinicMecCfg_t rightChCfg;
   media_info_t info;
   bool isAwinicParamSet;
   bool isInitOk;
}awinicCfg_t;
/***********************************************************************************************
 * *Name        :awinicMecProcess
 * *Description :awinic mec process
 * *Parameter   :Data ,configuration  ,data number
 * *Return Value: NULL
 * *Version: v1.0.0
 * ********************************************************************************************/
static ADSPResult is_supported_media_type(media_info_t *info)
{
     if(info->bits_per_sample !=16 && info->bits_per_sample != 24 &&info->bits_per_sample != 32)
    {
    	 AWLOGE(" Only 16 bit and 24 bit data supported. Received %u\n",info->bits_per_sample) ;
        return ADSP_EUNSUPPORTED;
    }
    if(info->data_is_signed !=1)
    {
    	AWLOGE(" Only Signed data supported. Received %u\n",info->data_is_signed) ;
        return ADSP_EUNSUPPORTED;
    }

    if(info->sampling_rate !=48000 &&info->sampling_rate != 8000 )
    {
    	AWLOGE("48K or 8K supported. Received %u\n",info->sampling_rate) ;
        return ADSP_EUNSUPPORTED;
    }
    if(info->num_channels !=1 &&info->num_channels != 2 )
    {
    	AWLOGE(" 1 or 2 channel supported. Received %u\n",info->num_channels) ;
        return ADSP_EUNSUPPORTED;
    }

    return ADSP_EOK;

}

ADSPResult AwinicGetSize(uint32_t *requestSize_ptr)
{
    if(requestSize_ptr == NULL)
    {
    	AWLOGE(" Pointer is NULL\n");
        return ADSP_EUNEXPECTED;
    }
    *requestSize_ptr = sizeof(awinicCfg_t);

    return ADSP_EOK;
}

ADSPResult AwinicInit(void *aw_ptr,media_info_t *info)
{
	awinicCfg_t *me_ptr = (awinicCfg_t*)aw_ptr;
    if(NULL == aw_ptr || NULL == info)
    {
    	AWLOGE(" Pointer is NULL\n") ;
    	me_ptr->isInitOk = false;
        return ADSP_EUNEXPECTED;
    }

    if(is_supported_media_type(info) != ADSP_EOK)
    {
        me_ptr->isInitOk = false;
        return ADSP_EUNSUPPORTED;
    }
    me_ptr->info.num_channels = info->num_channels;
    me_ptr->info.bits_per_sample = info->bits_per_sample;
    me_ptr->info.sampling_rate = info->sampling_rate;
    me_ptr->info.data_is_signed = info->data_is_signed;

    int ret ;

    ret = awinicAlgoInit(&me_ptr->leftChCfg);
    if(ret < 0)
    {
        me_ptr->isInitOk = false;
        return ADSP_EFAILED;
    }
    me_ptr->leftChCfg.dataBitNum = me_ptr->info.bits_per_sample -1;  //data bit un-include Signed bit 
    ret = awinicAlgoInit(&me_ptr->rightChCfg);
    if(ret < 0)
    {
        me_ptr->isInitOk = false;
        return ADSP_EFAILED;
    }
    me_ptr->rightChCfg.dataBitNum = me_ptr->info.bits_per_sample -1;

    me_ptr->isInitOk = true;
    return ADSP_EOK;
}

ADSPResult AwinicReset(void *aw_ptr)
{
    if(NULL == aw_ptr)
    {
        return ADSP_EUNEXPECTED;
    }

    awinicCfg_t *me_ptr = (awinicCfg_t*)aw_ptr;

    awinicAlgoReset(&me_ptr->leftChCfg);
    awinicAlgoReset(&me_ptr->rightChCfg);

    return 0;
}
ADSPResult AwinicSetParams(void *aw_ptr,void *param_ptr,uint16_t paramsBytesSize)
{
    int ret =0;
    if(aw_ptr == NULL || param_ptr == NULL )
    {
        return ADSP_EUNEXPECTED;
    }
    if(paramsBytesSize < 2*sizeof(awinicMecParams_t))
    {
    	AWLOGE("params size not right ,size is %ld\n",2*sizeof(awinicMecParams_t));
        return ADSP_EBADPARAM;
    }

    awinicCfg_t *me_ptr = (awinicCfg_t*)aw_ptr;
    awinicMecParams_t *param_set = (awinicMecParams_t*)param_ptr;

    ret = awinicAlgoSetParams(&me_ptr->leftChCfg,param_set);
    if(ret <0)
    {
    	AWLOGE("left params set failed\n");
    	me_ptr->isAwinicParamSet = false;
        return ADSP_EBADPARAM;
    }
    ret= awinicAlgoSetParams(&me_ptr->rightChCfg,param_set+1);
    if(ret <0)
    {
    	AWLOGE("left params set failed\n");
    	me_ptr->isAwinicParamSet = false;
        return ADSP_EBADPARAM;
    }
    me_ptr->isAwinicParamSet = true;
    return ADSP_EOK;
}
ADSPResult AwinicGetParams(void *aw_ptr,void *param_ptr,uint16_t *paramsBytesSize)
{
    int ret =0;
    if(aw_ptr == NULL || param_ptr == NULL )
    {
        return ADSP_EUNEXPECTED;
    }
    *paramsBytesSize = 2*sizeof(awinicMecParams_t);

    awinicCfg_t *me_ptr = (awinicCfg_t*)aw_ptr;
    awinicMecParams_t *param_get = (awinicMecParams_t*)param_ptr;

    ret = awinicAlgoGetParams(&me_ptr->leftChCfg,param_get);
    if(ret <0)
    {
    	AWLOGE("Left Params Get Failed\n");
        return ADSP_EBADPARAM;
    }
    ret= awinicAlgoGetParams(&me_ptr->rightChCfg,param_get+1);
    if(ret <0)
    {
    	AWLOGE("Right Params Get failed\n");
        return ADSP_EBADPARAM;
    }
    return ADSP_EOK;
}
ADSPResult AwinicEnd(void *aw_ptr)
{
    if(aw_ptr != NULL)
    {
        return ADSP_EUNEXPECTED;
    }
    return ADSP_EOK;
}

// data is 16 bits relate functions
static void awinicSinWave16Bit(int16_t *dataBuf,uint16_t dataCnt)
{
	uint16_t cnt;
    int16_t sinWave[32]= {0x0000,   0x027f,   0x04e6,   0x071c,   0x090d,   0x0aa5,   0x0bd3,   0x0c8e,
    		              0x0ccd,   0x0c8e,   0x0bd3,   0x0aa5,   0x090d,   0x071c,   0x04e6,   0x027f,
    		              0x0000,   0xfd81,   0xfb1a,   0xf8e4,   0xf6f3,   0xf55b,   0xf42d,   0xf372,
    		              0xf333,   0xf372,   0xf42d,   0xf55b,   0xf6f3,   0xf8e4,   0xfb1a,   0xfd81};

	for(cnt =0;cnt < dataCnt;cnt++ )
	{
		dataBuf[cnt] = sinWave[cnt%32];
	}
}
static void  awinicOneChProcessData16Bit(awinicCfg_t *cfg,void *dataBuf,uint16_t bytes,enableMode_t enable)
{
    int16_t *data = (int16_t*)dataBuf;

    int16_t *move_ptr = NULL;
    uint16_t num_samples = bytes>>1;

    uint16_t remain_samples_nums = num_samples;
    uint16_t current_buffer_num  = 0;
    move_ptr = data;
    //AWLOGD(" dataCnt %d \n",num_samples);
    //write_pcm16bit("../data/oneChdata.pcm",dataBuf,num_samples);
    if(enable == leftChEnable)
    {
        do
        {
            current_buffer_num = GetMinValue(remain_samples_nums,TMP_DATA_BUFFER_SIZE);
            awinicAlgoProcess16Bit(move_ptr,&cfg->leftChCfg,current_buffer_num);
            move_ptr += current_buffer_num;
            remain_samples_nums -= current_buffer_num;
        }while(remain_samples_nums);
    
    }else if(enable == rightChEnable){
        do
        {
            current_buffer_num = GetMinValue(remain_samples_nums,TMP_DATA_BUFFER_SIZE);
            awinicAlgoProcess16Bit(move_ptr,&cfg->rightChCfg,current_buffer_num);
            move_ptr += current_buffer_num;
            remain_samples_nums -= current_buffer_num;
        }while(remain_samples_nums);
       
    }else if(enable == sinWave)
    {
    	awinicSinWave16Bit(dataBuf,num_samples);
    }else{
        AWLOGD("Enable Value Is not Support\n");
    }
    
}

static uint16_t awinicSeparationRL16Bit(int16_t *data,int16_t *leftChData,int16_t *rightChData,uint16_t dataCnt)
{
    uint16_t cnt = dataCnt >> 1;
    uint16_t i;
    for(i=0;i<cnt;i++)
    {
        leftChData[i] = data[2*i];
        rightChData[i]= data[2*i + 1];
    }
    return cnt;
}

static void awinicMergeRL16Bit(int16_t *data ,int16_t *leftChData,int16_t *rightChData,uint16_t dataCnt)
{
    uint16_t i;
    for(i=0;i<dataCnt;i++)
    {
        data[2*i]   = leftChData[i];
        data[2*i+1] = rightChData[i];
    }
}
static void awinicMix16Bit(int16_t *leftChData,int16_t *rightChData,uint16_t dataCnt)
{
    int32_t result;
    uint16_t cnt;

    for(cnt=0;cnt<dataCnt;cnt++)
    {
    	result = (int32_t)leftChData[cnt]+rightChData[cnt];
    	result = result/2;
    	leftChData[cnt] = result;
    	rightChData[cnt]= result;
    }
}
static void awinicTwoChAlgo16BitProcess(awinicCfg_t *cfg,int16_t *leftChData,int16_t *rightChData,uint16_t dataCnt,enableMode_t enable)
{
	//AWLOGD(" enter,16 bit echo ch dataCnt is %d \n",dataCnt);

    if(enable == twoChEnable)
    {
        awinicAlgoProcess16Bit(leftChData,&cfg->leftChCfg,dataCnt);
        awinicAlgoProcess16Bit(rightChData,&cfg->rightChCfg,dataCnt); 
    }
    else if(enable == leftChEnable)
    {
        awinicAlgoProcess16Bit(leftChData,&cfg->leftChCfg,dataCnt);
    }else if(enable == rightChEnable)
    {
        awinicAlgoProcess16Bit(rightChData,&cfg->rightChCfg,dataCnt);
    }else if(enable == twoChMixEnable)
    {
    	awinicMix16Bit(leftChData,rightChData,dataCnt);
        awinicAlgoProcess16Bit(leftChData,&cfg->leftChCfg,dataCnt);
        awinicAlgoProcess16Bit(rightChData,&cfg->rightChCfg,dataCnt);
    }else if(enable == sinWave)
    {
    	awinicSinWave16Bit(leftChData,dataCnt);
    	awinicSinWave16Bit(rightChData,dataCnt);
    }
    else{
        AWLOGD("Enable Value is Unsupport,Bypass \n");
    }
}

static void  awinicTwoChProcessData16Bit(awinicCfg_t *cfg,void *dataBuf,uint16_t bytes,enableMode_t enable)
{
	//AWLOGD(" enter ,bytes is %d\n",bytes>>1);
    int16_t leftChData[TMP_DATA_BUFFER_SIZE];
    int16_t rightChData[TMP_DATA_BUFFER_SIZE];
 
    
    int16_t *data = (int16_t*)dataBuf;

    int16_t *move_ptr = NULL;

    //Get 16bit data Sample number
    uint16_t num_samples = bytes>>1;

    uint16_t remain_samples_nums = num_samples;
    uint16_t current_buffer_num  = 0;

    //Get Two Channel Data size
    uint16_t two_ch_buf_size = TMP_DATA_BUFFER_SIZE << 1;

    uint16_t echo_ch_data_size;
    move_ptr = data;

    do
    {
    	//Get Current buffer data number
        current_buffer_num = GetMinValue(remain_samples_nums,two_ch_buf_size);
        //Separate Two Channel Data to Left and Right
        echo_ch_data_size = awinicSeparationRL16Bit(move_ptr,leftChData,rightChData,current_buffer_num);
        //Process Channel data process
        awinicTwoChAlgo16BitProcess(cfg,leftChData,rightChData,echo_ch_data_size,enable);
        //Merge Two Channel Data to one Pcm data
        awinicMergeRL16Bit(move_ptr,leftChData,rightChData,echo_ch_data_size);
        //Get remain Data number
        remain_samples_nums -= current_buffer_num;
        //Jump to Next Buffer
        move_ptr += current_buffer_num;
    }while(remain_samples_nums);
}

static void awinicProcessData16Bit(awinicCfg_t *cfg,void *dataBuf,uint16_t bytes,enableMode_t enable)
{
	//AWLOGD("enter \n");
    if(cfg->info.num_channels ==2)
    {
        awinicTwoChProcessData16Bit(cfg,dataBuf,bytes,enable);
    }else{
        awinicOneChProcessData16Bit(cfg,dataBuf,bytes,enable);
    }
}
//32bit data funuctions
static void awinicSinWave32Bit(int32_t *dataBuf,uint16_t dataCnt,uint16_t bitWidth)
{
	uint16_t cnt;
    int32_t sinWave[32]= {0x00000000,   0x027f0000,   0x04e60000,   0x071c0000,   0x090d0000,   0x0aa50000,   0x0bd30000,   0x0c8e0000,
    		              0x0ccd0000,   0x0c8e0000,   0x0bd30000,   0x0aa50000,   0x090d0000,   0x071c0000,   0x04e60000,   0x027f0000,
    		              0x00000000,   0xfd810000,   0xfb1a0000,   0xf8e40000,   0xf6f30000,   0xf55b0000,   0xf42d0000,   0xf3720000,
    		              0xf3330000,   0xf3720000,   0xf42d0000,   0xf55b0000,   0xf6f30000,   0xf8e40000,   0xfb1a0000,   0xfd810000};

    if(bitWidth == 32)
    {
	   for(cnt =0;cnt < dataCnt;cnt++ )
	   {
		   dataBuf[cnt] = sinWave[cnt%32];
	   }
    }else
    {
 	   for(cnt =0;cnt < dataCnt;cnt++ )
 	   {
 		   dataBuf[cnt] = sinWave[cnt%32]>>8;
 	   }
    }
}
static void  awinicOneChProcessData32Bit(awinicCfg_t *cfg,void *dataBuf,uint16_t bytes,enableMode_t enable)
{
    int32_t *data = (int32_t*)dataBuf;

    int32_t *move_ptr = NULL;
    uint16_t num_samples = bytes>>2;

    uint16_t remain_samples_nums = num_samples;
    uint16_t current_buffer_num  = 0;
    move_ptr = data;

    if(enable == leftChEnable)
    {
        do
        {
            current_buffer_num = GetMinValue(remain_samples_nums,TMP_DATA_BUFFER_SIZE);
            awinicAlgoProcess32Bit(move_ptr,&cfg->leftChCfg,current_buffer_num);
            move_ptr += current_buffer_num;
            remain_samples_nums -= current_buffer_num;
        }while(remain_samples_nums);
    
    }else if(enable == rightChEnable){
        do
        {
            current_buffer_num = GetMinValue(remain_samples_nums,TMP_DATA_BUFFER_SIZE);
            awinicAlgoProcess32Bit(move_ptr,&cfg->rightChCfg,current_buffer_num);
            move_ptr += current_buffer_num;
            remain_samples_nums -= current_buffer_num;
        }while(remain_samples_nums);
       
    }else if(enable == sinWave)
    {
    	awinicSinWave32Bit(dataBuf,num_samples,cfg->info.bits_per_sample);
    }
    else{
    	AWLOGE("Enable Value is Unsuppot\n");
    }
}    
static uint16_t awinicSeparationRL32Bit(int32_t *data,int32_t *leftChData,int32_t *rightChData,uint16_t dataCnt)
{
    uint16_t cnt = dataCnt >> 1;
    uint16_t i;
    for(i=0;i<cnt;i++)
    {
        leftChData[i] = data[2*i];
        rightChData[i]= data[2*i + 1];
    }
    return cnt;
}
static void awinicMix32Bit(int32_t *leftChData,int32_t *rightChData,uint16_t dataCnt)
{
    int64_t result;
    uint16_t cnt;
    for(cnt=0;cnt<dataCnt;cnt++)
    {
    	result = (int64_t)leftChData[cnt]+rightChData[cnt];
    	result = result/2;
    	leftChData[cnt] = result;
    	rightChData[cnt]= result;
    }
}
static void awinicTwoChAlgo32BitProcess(awinicCfg_t *cfg,int32_t *leftChData,int32_t *rightChData,uint16_t dataCnt,enableMode_t enable)
{
    if(enable == twoChEnable)
    {
        awinicAlgoProcess32Bit(leftChData,&cfg->leftChCfg,dataCnt);
        awinicAlgoProcess32Bit(rightChData,&cfg->rightChCfg,dataCnt); 
    }
    else if(enable == leftChEnable)
    {
        awinicAlgoProcess32Bit(leftChData,&cfg->leftChCfg,dataCnt);
    }else if(enable == rightChEnable)
    {
        awinicAlgoProcess32Bit(rightChData,&cfg->rightChCfg,dataCnt);
    }else if(enable == twoChMixEnable)
    {
    	awinicMix32Bit(leftChData,rightChData,dataCnt);
        awinicAlgoProcess32Bit(leftChData,&cfg->leftChCfg,dataCnt);
        awinicAlgoProcess32Bit(rightChData,&cfg->rightChCfg,dataCnt);
    }else if(enable == sinWave)
    {
    	awinicSinWave32Bit(leftChData,dataCnt,cfg->info.bits_per_sample);
    	awinicSinWave32Bit(rightChData,dataCnt,cfg->info.bits_per_sample);
    }
    else{
        AWLOGE("Enable Value is Unsuppot\n");
    }
}
static void awinicMergeRL32Bit(int32_t *data ,int32_t *leftChData,int32_t *rightChData,uint16_t dataCnt)
{
    uint16_t i;
    for(i=0;i<dataCnt;i++)
    {
        data[2*i]   = leftChData[i];
        data[2*i+1] = rightChData[i];
    }
}
static void  awinicTwoChProcessData32Bit(awinicCfg_t *cfg,void *dataBuf,uint16_t bytes,enableMode_t enable)
{
    int32_t leftChData[TMP_DATA_BUFFER_SIZE];
    int32_t rightChData[TMP_DATA_BUFFER_SIZE];
 
    
    int32_t *data = (int32_t*)dataBuf;

    int32_t *move_ptr = NULL;
    //Get 32bit Data sample number
    uint16_t num_samples = bytes>>2;

    uint16_t remain_samples_nums = num_samples;
    uint16_t current_buffer_num  = 0;

    //Get Echo Two Channel Data numberr
    uint16_t two_ch_buf_size = TMP_DATA_BUFFER_SIZE << 1;
    uint16_t echo_ch_data_size;
    move_ptr = data;

    do
    {
    	//Get current process data number
        current_buffer_num = GetMinValue(remain_samples_nums,two_ch_buf_size);
        //Separate Two Channel Data to LeftBuf and Right Buf
        echo_ch_data_size = awinicSeparationRL32Bit(move_ptr,leftChData,rightChData,current_buffer_num);
        //process echo Channel Data
        awinicTwoChAlgo32BitProcess(cfg,leftChData,rightChData,echo_ch_data_size,enable);
        //Merge Two Cahnned to PCM
        awinicMergeRL32Bit(move_ptr,leftChData,rightChData,echo_ch_data_size);
        //Get remain sample number
        remain_samples_nums -= current_buffer_num;
        //Get next Buf Pointer
        move_ptr += current_buffer_num;
    }while(remain_samples_nums);
}

static void awinicProcessData32Bit(awinicCfg_t *cfg,void *dataBuf,uint16_t bytes,enableMode_t enable)
{
    if(cfg->info.num_channels ==2)
    {
        awinicTwoChProcessData32Bit(cfg,dataBuf,bytes,enable);
    }else{
        awinicOneChProcessData32Bit(cfg,dataBuf,bytes,enable);
    }
}
ADSPResult AwinicHandle(void *aw_ptr,void *dataBuf,uint16_t bytes,enableMode_t enable)
{

    if(aw_ptr == NULL || dataBuf == NULL )
    {
    	AWLOGE(" Pointer Is NULL\n");
        return ADSP_EUNEXPECTED;
    }
    awinicCfg_t *me_ptr = (awinicCfg_t*)aw_ptr;
    
    if(me_ptr->isInitOk == false || me_ptr->isAwinicParamSet == false)
    {
    	AWLOGE(" Init Or SetParams Is Failed\n");
    	return ADSP_EBADPARAM;
    }
    if(bytes == 0 || enable == byPass)
    {
    	AWLOGD(" Bypass Mode\n");
        return ADSP_EOK;
    }


    if(me_ptr->info.bits_per_sample == 16)
    {
        awinicProcessData16Bit(me_ptr,dataBuf,bytes,enable);
    }else
    {
        awinicProcessData32Bit(me_ptr,dataBuf,bytes,enable);
    }

    return ADSP_EOK;
}

